#include "thread.h"
#include "global.h"
#include "memory.h"
#include "string.h"
#include "list.h"
#include "debug.h"
#include "interrupt.h"


#define PAGE_SIZE 4096 //页的大小，4096字节，即4K。

static struct list ready_tasks;
static struct list all_tasks;
static struct list_elem* current;
static struct task_struct* mainThread;

extern switch_to(struct task_struct* current, struct task_struct* next);

static void make_main_thread(void)
{
    mainThread = current_thread();
    init_thread(mainThread,"main",30);
    ASSERT( !elem_find(&all_tasks,&mainThread->allListElement) );
    list_append(&all_tasks,&mainThread->allListElement);
}

void schedule(void)
{
    struct task_struct* currentThread = current_thread();
    if( currentThread->status == TASK_RUNNING){
        ASSERT( !elem_find(&ready_tasks,&currentThread->generalListElement));
        list_append(&ready_tasks,&currentThread->generalListElement);
        currentThread->status = TASK_READY;
        currentThread->tick = currentThread->priority;

    }else{
        //tbd
    }
   
    struct list_elem* nextElem = list_pop(&ready_tasks);
    if(nextElem != NULL){
        struct task_struct*  next = member_to_entry(struct task_struct,generalListElement,nextElem);
        next->status = TASK_RUNNING;
        switch_to(currentThread,next);
    }
    
    
}

void thread_block(enum task_status status)
{
    ASSERT(status == TASK_BLOCKED || status == TASK_HANGING || status == TASK_WATTING );
    enum intr_status oldStatus = intr_disable(); 
    struct task_struct *current = current_thread();

    current->status = status;
    schedule();

    set_intr_status(oldStatus);
}

void thread_unblock(struct task_struct *pthread)
{
    enum intr_status oldStatus = intr_disable(); 
    enum task_status status = pthread->status;

    ASSERT(status == TASK_BLOCKED || status == TASK_HANGING || status == TASK_WATTING );
    ASSERT( !elem_find(&ready_tasks, &pthread->generalListElement));

    pthread->status = TASK_READY;
    list_insert_before(ready_tasks.head.next, &pthread->generalListElement);

    set_intr_status(oldStatus);
}

void thread_init(void)
{
    put_str("thread init start!\n");
    list_init(&ready_tasks);
    list_init(&all_tasks);
    make_main_thread();
    put_str("thread init end!\n");
}


struct task_struct* current_thread(void)
{
    uint32_t esp;
    asm volatile ("mov %%esp,%0" : "=g"(esp));
    return (struct task_struct*) (esp & 0xfffff000);
}

/* 由kernel_thread去执行function(func_arg) , 这个函数就是线程中去开启我们要运行的函数*/
static void kernel_thread(thread_func* function, void* func_arg) {
    intr_enable();
    function(func_arg); 
}


void thread_create(struct task_struct* pthread, thread_func function, void* func_arg)
{
     pthread->self_kstack =(uint32_t*)( (uint32_t) pthread->self_kstack - sizeof(struct intr_stack ));
     pthread->self_kstack =(uint32_t*)( (uint32_t) pthread->self_kstack - sizeof(struct thread_stack));
     
     struct thread_stack * threadStrack = (struct thread_stack *)pthread->self_kstack;
     threadStrack->ebp = 0;
     threadStrack->ebx = 0;
     threadStrack->edi = 0;
     threadStrack->esi = 0;

     threadStrack->eip = kernel_thread;

     /**
      * build stack for C-Func call.
      * retaddr is not used, just a dummy value
     */
     threadStrack->function = function;
     threadStrack->func_arg = func_arg;
     threadStrack->unused_retaddr = 0 ;

}

void init_thread(struct task_struct* pthread, char* name, int prio)
{   
    memset(pthread,0,sizeof(*pthread));
    strcpy(pthread->name,name);
    pthread->priority = prio;
    pthread->tick = prio;
    pthread->escapTick = 0;
    pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PAGE_SIZE);
    pthread->stack_magic=STACK_MAGIC;
    pthread->pgaddr = NULL;
    if(pthread == mainThread){
        pthread->status = TASK_RUNNING;
    }else{
        pthread->status = TASK_READY;
    }
}

struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg)
{
    struct task_struct* pthread = malloc_kernel_page(1);
    init_thread(pthread,name,prio);
    thread_create(pthread,function,func_arg);
    
    ASSERT( !elem_find( &ready_tasks,&(pthread->generalListElement)));
    list_append(&ready_tasks,&pthread->generalListElement);

    ASSERT( !elem_find( &all_tasks,&pthread->allListElement));
    list_append(&all_tasks,&pthread->allListElement);
   return pthread;    
}